﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Drawing;

namespace MipsPipelineSimulator
{
    class MEMStage
    {
        //能否运行
        static public int enableRun = 0;

        static private ManualResetEvent manuaEevent = new ManualResetEvent(false);

        static private Thread memThread = null;

        static public Point[] position = new Point[16];

        static private MainForm mainForm = null;

        static public Code code;

        static public object[] args = null;

        static private void ThreadFun()
        {
            if (enableRun < 0)
            {
                manuaEevent.Set();
                return;
            }
             MEMStage.enableRun--;

             object obj1 = null;
             string str1 = null;
             string str2 = null;

             switch (code.codeType)
             {
                 case CodeType.ADD:
                 case CodeType.SUB:
                 case CodeType.MUL:
                 case CodeType.SLT:
                 case CodeType.AND:
                 case CodeType.NOR:
                 case CodeType.OR:
                 case CodeType.XOR:
                 case CodeType.SLLV:
                 case CodeType.SRAV:
                 case CodeType.SRLV:
                 case CodeType.ADDI:
                 case CodeType.SLTI:
                 case CodeType.ANDI:
                 case CodeType.ORI:
                 case CodeType.XORI:
                 case CodeType.SLL:
                 case CodeType.SRA:
                 case CodeType.SRL:
                     {
                         str1 = CommonTool.NumToStr(TypeCode.Int32, MEMStage.args[0], "D", false);
                         break;
                     }
                 case CodeType.DADD:
                 case CodeType.DSUB:
                 case CodeType.DSLLV:
                 case CodeType.DSRLV:
                 case CodeType.DSRAV:
                 case CodeType.DADDI:
                 case CodeType.DSLL:
                 case CodeType.DSRL:
                 case CodeType.DSRA:
                     {
                         str1 = CommonTool.NumToStr(TypeCode.Int64, MEMStage.args[0], "D", false);
                         break;
                     }
                 case CodeType.ADD_S:
                 case CodeType.SUB_S:
                 case CodeType.MUL_S:
                 case CodeType.DIV_S:
                     {
                         str1 = CommonTool.NumToStr(TypeCode.Single, MEMStage.args[0], "D", false);
                         break;
                     }
                 case CodeType.ADD_D:
                 case CodeType.SUB_D:
                 case CodeType.MUL_D:
                 case CodeType.DIV_D:
                     {
                         str1 = CommonTool.NumToStr(TypeCode.Double, MEMStage.args[0], "D", false);
                         break;
                     }
                 case CodeType.ADDU:
                 case CodeType.SUBU:
                 case CodeType.SLTU:
                 case CodeType.ADDIU:
                 case CodeType.SLTIU:
                     {
                         str1 = CommonTool.NumToStr(TypeCode.UInt32, MEMStage.args[0], "D", false);
                         break;
                     }
                 case CodeType.DADDU:
                 case CodeType.DSUBU:
                 case CodeType.DADDIU:
                     {
                         str1 = CommonTool.NumToStr(TypeCode.UInt64, MEMStage.args[0], "D", false);
                         break;
                     }
                 case CodeType.BEQZ:
                 case CodeType.BNEZ:
                     {
                         break;
                     }
                 case CodeType.LB:
                     {
                         obj1 = mainForm.commandForm.memoryControl.GetMemoryValue((Int64)MEMStage.args[0], TypeCode.SByte);
                         str1 = CommonTool.NumToStr(TypeCode.SByte, obj1, "D", false);
                         str2 = CommonTool.NumToStr(TypeCode.Int64, MEMStage.args[0], "D", false);
                         break;
                     }
                 case CodeType.LH:
                     {
                         obj1 = mainForm.commandForm.memoryControl.GetMemoryValue((Int64)MEMStage.args[0], TypeCode.Int16);
                         str1 = CommonTool.NumToStr(TypeCode.Int16, obj1, "D", false);
                         str2 = CommonTool.NumToStr(TypeCode.Int64, MEMStage.args[0], "D", false);
                         break;
                     }
                 case CodeType.LW:
                     {
                         obj1 = mainForm.commandForm.memoryControl.GetMemoryValue((Int64)MEMStage.args[0], TypeCode.Int32);
                         str1 = CommonTool.NumToStr(TypeCode.Int32, obj1, "D", false);
                         str2 = CommonTool.NumToStr(TypeCode.Int64, MEMStage.args[0], "D", false); 
                         break;
                     }
                 case CodeType.LBU:
                     {
                         obj1 = mainForm.commandForm.memoryControl.GetMemoryValue((Int64)MEMStage.args[0], TypeCode.Byte);
                         str1 = CommonTool.NumToStr(TypeCode.Byte, obj1, "D", false);
                         str2 = CommonTool.NumToStr(TypeCode.Int64, MEMStage.args[0], "D", false);
                         break;
                     }
                 case CodeType.LHU:
                     {
                         obj1 = mainForm.commandForm.memoryControl.GetMemoryValue((Int64)MEMStage.args[0], TypeCode.UInt16);
                         str1 = CommonTool.NumToStr(TypeCode.UInt16, obj1, "D", false);
                         str2 = CommonTool.NumToStr(TypeCode.Int64, MEMStage.args[0], "D", false);
                         break;
                     }
                 case CodeType.LWU:
                     {
                         obj1 = mainForm.commandForm.memoryControl.GetMemoryValue((Int64)MEMStage.args[0], TypeCode.UInt32);
                         str1 = CommonTool.NumToStr(TypeCode.UInt32, obj1, "D", false);
                         str2 = CommonTool.NumToStr(TypeCode.Int64, MEMStage.args[0], "D", false);
                         break;
                     }
                 case CodeType.LD:
                     {
                         obj1 = mainForm.commandForm.memoryControl.GetMemoryValue((Int64)MEMStage.args[0], TypeCode.UInt64);
                         str1 = CommonTool.NumToStr(TypeCode.UInt64, obj1, "D", false);
                         str2 = CommonTool.NumToStr(TypeCode.Int64, MEMStage.args[0], "D", false);
                         break;
                     }
                 case CodeType.LDC1:
                     {
                         obj1 = mainForm.commandForm.memoryControl.GetMemoryValue((Int64)MEMStage.args[0], TypeCode.Single);
                         str1 = CommonTool.NumToStr(TypeCode.Single, obj1, "D", false);
                         str2 = CommonTool.NumToStr(TypeCode.Int64, MEMStage.args[0], "D", false);
                         break;
                     }
                 case CodeType.LWC1:
                     {
                         obj1 = mainForm.commandForm.memoryControl.GetMemoryValue((Int64)MEMStage.args[0], TypeCode.Double);
                         str1 = CommonTool.NumToStr(TypeCode.Double, obj1, "D", false);
                         str2 = CommonTool.NumToStr(TypeCode.Int64, MEMStage.args[0], "D", false);
                         break;
                     }
                 case CodeType.SD:
                     {
                         str1 = CommonTool.NumToStr(TypeCode.Int64, MEMStage.args[0], "D", false);
                         str2 = CommonTool.NumToStr(TypeCode.Int64, MEMStage.args[1], "D", false);
                         mainForm.commandForm.memoryControl.SetMemoryValue((Int64)MEMStage.args[0], TypeCode.Int64, MEMStage.args[1]);
                         break;
                     }
                 case CodeType.SB:
                     {
                         str1 = CommonTool.NumToStr(TypeCode.Int64, MEMStage.args[0], "D", false);
                         str2 = CommonTool.NumToStr(TypeCode.SByte, MEMStage.args[1], "D", false);
                         mainForm.commandForm.memoryControl.SetMemoryValue((Int64)MEMStage.args[0], TypeCode.SByte, MEMStage.args[1]);
                         break;
                     }
                 case CodeType.SH:
                     {
                         str1 = CommonTool.NumToStr(TypeCode.Int64, MEMStage.args[0], "D", false);
                         str2 = CommonTool.NumToStr(TypeCode.Int16, MEMStage.args[1], "D", false);
                         mainForm.commandForm.memoryControl.SetMemoryValue((Int64)MEMStage.args[0], TypeCode.Int16, MEMStage.args[1]);
                         break;
                     }
                 case CodeType.SW:
                     {
                         str1 = CommonTool.NumToStr(TypeCode.Int64, MEMStage.args[0], "D", false);
                         str2 = CommonTool.NumToStr(TypeCode.Int32, MEMStage.args[1], "D", false);
                         mainForm.commandForm.memoryControl.SetMemoryValue((Int64)MEMStage.args[0], TypeCode.Int32, MEMStage.args[1]);
                         break;
                     }
                 case CodeType.SDC1:
                     {
                         str1 = CommonTool.NumToStr(TypeCode.Int64, MEMStage.args[0], "D", false);
                         str2 = CommonTool.NumToStr(TypeCode.Single, MEMStage.args[1], "D", false);
                         mainForm.commandForm.memoryControl.SetMemoryValue((Int64)MEMStage.args[0], TypeCode.Single, MEMStage.args[1]);
                         break;
                     }
                 case CodeType.SWC1:
                     {
                         str1 = CommonTool.NumToStr(TypeCode.Int64, MEMStage.args[0], "D", false);
                         str2 = CommonTool.NumToStr(TypeCode.Double, MEMStage.args[1], "D", false);
                         mainForm.commandForm.memoryControl.SetMemoryValue((Int64)MEMStage.args[0], TypeCode.Double, MEMStage.args[1]);
                         break;
                     }
             }

             switch (code.codeType)
             {
                 case CodeType.ADD:
                 case CodeType.SUB:
                 case CodeType.MUL:
                 case CodeType.SLT:
                 case CodeType.AND:
                 case CodeType.NOR:
                 case CodeType.OR:
                 case CodeType.XOR:
                 case CodeType.SLLV:
                 case CodeType.SRAV:
                 case CodeType.SRLV:
                 case CodeType.ADDI:
                 case CodeType.SLTI:
                 case CodeType.ANDI:
                 case CodeType.ORI:
                 case CodeType.XORI:
                 case CodeType.SLL:
                 case CodeType.SRA:
                 case CodeType.SRL:
                 case CodeType.DADD:
                 case CodeType.DSUB:
                 case CodeType.DSLLV:
                 case CodeType.DSRLV:
                 case CodeType.DSRAV:
                 case CodeType.DADDI:
                 case CodeType.DSLL:
                 case CodeType.DSRL:
                 case CodeType.DSRA:
                 case CodeType.ADD_S:
                 case CodeType.SUB_S:
                 case CodeType.MUL_S:
                 case CodeType.DIV_S:
                 case CodeType.ADD_D:
                 case CodeType.SUB_D:
                 case CodeType.MUL_D:
                 case CodeType.DIV_D:
                 case CodeType.ADDU:
                 case CodeType.SUBU:
                 case CodeType.SLTU:
                 case CodeType.ADDIU:
                 case CodeType.SLTIU:
                 case CodeType.DADDU:
                 case CodeType.DSUBU:
                 case CodeType.DADDIU:
                     {
                         Thread thread1 = CommonTool.StartDrawPathsThread(new DrawPaths(
                         new Animate[3] { 
                                CommonTool.SetAnimate(MEMStage.position[0],MEMStage.position[1],Direction.LeftToRight,str1,true,mainForm.GetGraphics()),
                                CommonTool.SetAnimate(MEMStage.position[1],MEMStage.position[7],Direction.TopToBottom,str1,true,mainForm.GetGraphics()),
                                CommonTool.SetAnimate(MEMStage.position[7],MEMStage.position[8],Direction.LeftToRight,str1,true,mainForm.GetGraphics())
                            }, MEMStage.mainForm));
                         Thread thread2 = CommonTool.StartDrawPathsThread(new DrawPaths(
                         new Animate[1] { 
                                CommonTool.SetAnimate(MEMStage.position[9],MEMStage.position[10],Direction.LeftToRight,code.codeType.ToString(),true,mainForm.GetGraphics())
                            }, MEMStage.mainForm));

                         thread1.Join();
                         thread1 = null;
                         thread2.Join();
                         thread2 = null;

                         Thread thread3 = CommonTool.StartBlinkThread(new Blink(
                          str1, MEMStage.position[13], DrawTool.blinkTimes, DrawTool.blinkSpanTime, DrawTool.blinkHideTime, MEMStage.mainForm));

                         Thread thread4 = CommonTool.StartBlinkThread(new Blink(
                            code.codeType.ToString(), MEMStage.position[14], DrawTool.blinkTimes, DrawTool.blinkSpanTime, DrawTool.blinkHideTime, MEMStage.mainForm));

                         thread3.Join();
                         thread3 = null;
                         thread4.Join();
                         thread4 = null;

                         CommonTool.DrawPath(MEMStage.mainForm,
                          new DrawPath[2]{
                                 new DrawPath(mainForm.GetGraphics(),new Point[4]{MEMStage.position[0],MEMStage.position[1],MEMStage.position[7],MEMStage.position[8]},DrawTool.orignalPen),
                                 new DrawPath(mainForm.GetGraphics(),new Point[2]{MEMStage.position[9],MEMStage.position[10]},DrawTool.orignalPen)
                            });

                         WBStage.Wait();
                         WBStage.code = MEMStage.code;
                         WBStage.args = new object[1] { MEMStage.args[0] };
                         WBStage.enableRun++;
                         break;
                     }
                 case CodeType.BEQZ:
                 case CodeType.BNEZ:
                     {
                         Thread thread1 = CommonTool.StartDrawPathsThread(new DrawPaths(
                         new Animate[1] { 
                                CommonTool.SetAnimate(MEMStage.position[9],MEMStage.position[10],Direction.LeftToRight,code.codeType.ToString(),true,mainForm.GetGraphics())
                            }, MEMStage.mainForm));
                         thread1.Join();
                         thread1 = null;

                         Thread thread2 = CommonTool.StartBlinkThread(new Blink(
                            code.codeType.ToString(), MEMStage.position[14], DrawTool.blinkTimes, DrawTool.blinkSpanTime, DrawTool.blinkHideTime, MEMStage.mainForm));
                         thread2.Join();
                         thread2 = null;

                         CommonTool.DrawPath(MEMStage.mainForm,
                           new DrawPath[1]{
                                 new DrawPath(mainForm.GetGraphics(),new Point[2]{MEMStage.position[9],MEMStage.position[10]},DrawTool.orignalPen)
                            });

                         WBStage.Wait();
                         WBStage.code = MEMStage.code;
                         WBStage.args = null;
                         WBStage.enableRun++;
                         break;
                     }
                 case CodeType.SD:
                 case CodeType.SB:
                 case CodeType.SH:
                 case CodeType.SW:
                 case CodeType.SDC1:
                 case CodeType.SWC1:
                     {
                         Thread thread1 = CommonTool.StartDrawPathsThread(new DrawPaths(
                         new Animate[1] { 
                                CommonTool.SetAnimate(MEMStage.position[0],MEMStage.position[2],Direction.LeftToRight,str1,true,mainForm.GetGraphics())
                            }, MEMStage.mainForm));
                         Thread thread2 = CommonTool.StartDrawPathsThread(new DrawPaths(
                                                  new Animate[3] { 
                                CommonTool.SetAnimate(MEMStage.position[5],MEMStage.position[6],Direction.LeftToRight,str2,true,mainForm.GetGraphics()),
                                CommonTool.SetAnimate(MEMStage.position[6],MEMStage.position[3],Direction.BottomToTop,str2,true,mainForm.GetGraphics()),
                                CommonTool.SetAnimate(MEMStage.position[3],MEMStage.position[4],Direction.LeftToRight,str2,true,mainForm.GetGraphics())
                            }, MEMStage.mainForm));
                         Thread thread3 = CommonTool.StartDrawPathsThread(new DrawPaths(
                         new Animate[1] { 
                                CommonTool.SetAnimate(MEMStage.position[9],MEMStage.position[10],Direction.LeftToRight,code.codeType.ToString(),true,mainForm.GetGraphics())
                            }, MEMStage.mainForm));
                         thread1.Join();
                         thread1 = null;
                         thread2.Join();
                         thread2 = null;
                         thread3.Join();
                         thread3 = null;
                         
                         Thread thread4 = CommonTool.StartBlinkThread(new Blink(
                            code.codeType.ToString(), MEMStage.position[14], DrawTool.blinkTimes, DrawTool.blinkSpanTime, DrawTool.blinkHideTime, MEMStage.mainForm));

                         thread4.Join();
                         thread4 = null;

                         CommonTool.DrawPath(MEMStage.mainForm,
                          new DrawPath[3]{
                                 new DrawPath(mainForm.GetGraphics(),new Point[2]{MEMStage.position[0],MEMStage.position[2]},DrawTool.orignalPen),
                                 new DrawPath(mainForm.GetGraphics(),new Point[4]{MEMStage.position[5],MEMStage.position[6],MEMStage.position[3],MEMStage.position[4]},DrawTool.orignalPen),
                                 new DrawPath(mainForm.GetGraphics(),new Point[2]{MEMStage.position[9],MEMStage.position[10]},DrawTool.orignalPen)
                            });

                         WBStage.Wait();
                         WBStage.code = MEMStage.code;
                         WBStage.args = null;
                         WBStage.enableRun++;
                         break;
                     }
                 case CodeType.LB:
                 case CodeType.LH:
                 case CodeType.LW:
                 case CodeType.LD:
                 case CodeType.LBU:
                 case CodeType.LHU:
                 case CodeType.LWU:
                 case CodeType.LDC1:
                 case CodeType.LWC1:
                     {
                         Thread thread1 = CommonTool.StartDrawPathsThread(new DrawPaths(
                                                  new Animate[1] { 
                                CommonTool.SetAnimate(MEMStage.position[0],MEMStage.position[2],Direction.LeftToRight,str2,true,mainForm.GetGraphics())
                            }, MEMStage.mainForm));
 
                         Thread thread3 = CommonTool.StartDrawPathsThread(new DrawPaths(
                         new Animate[1] { 
                                CommonTool.SetAnimate(MEMStage.position[9],MEMStage.position[10],Direction.LeftToRight,code.codeType.ToString(),true,mainForm.GetGraphics())
                            }, MEMStage.mainForm));
                         thread1.Join();
                         thread1 = null;

                         Thread thread2 = CommonTool.StartDrawPathsThread(new DrawPaths(
                                                  new Animate[1] { 
                                CommonTool.SetAnimate(MEMStage.position[15],MEMStage.position[11],Direction.LeftToRight,str1,true,mainForm.GetGraphics())
                            }, MEMStage.mainForm));

                         thread2.Join();
                         thread2 = null;
                         thread3.Join();
                         thread3 = null;

                         Thread thread4 = CommonTool.StartBlinkThread(new Blink(
                            code.codeType.ToString(), MEMStage.position[14], DrawTool.blinkTimes, DrawTool.blinkSpanTime, DrawTool.blinkHideTime, MEMStage.mainForm));
                         Thread thread5 = CommonTool.StartBlinkThread(new Blink(
                            str1, MEMStage.position[12], DrawTool.blinkTimes, DrawTool.blinkSpanTime, DrawTool.blinkHideTime, MEMStage.mainForm));

                         thread4.Join();
                         thread4 = null;
                         thread5.Join();
                         thread5 = null;

                         CommonTool.DrawPath(MEMStage.mainForm,
                          new DrawPath[3]{
                              new DrawPath(mainForm.GetGraphics(),new Point[2]{MEMStage.position[0],MEMStage.position[2]},DrawTool.orignalPen),
                              new DrawPath(mainForm.GetGraphics(),new Point[2]{MEMStage.position[15],MEMStage.position[11]},DrawTool.orignalPen), 
                              new DrawPath(mainForm.GetGraphics(),new Point[2]{MEMStage.position[9],MEMStage.position[10]},DrawTool.orignalPen)
                            });

                         WBStage.Wait();
                         WBStage.code = MEMStage.code;
                         WBStage.args = new object[1] { obj1};
                         WBStage.enableRun++;
                         break;
                     }
             }
             manuaEevent.Set();
        }

        static public void Start(MainForm param)
        {
            mainForm = param;
            //开始标志
            manuaEevent.Reset();
            memThread = new Thread(new ThreadStart(ThreadFun));
            memThread.Start();
        }

        static public void Initialize()
        {
            MEMStage.enableRun = -1;
            manuaEevent = new ManualResetEvent(false);
        }

        static public void Wait()
        {
            manuaEevent.WaitOne();
        }

        static public void Close()
        {
            if (memThread != null && memThread.IsAlive)
            {
                memThread.Join();
            }
        }
    }
}
